package org.zjvis.datascience.common.util;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Maps;
import javafx.util.Pair;
import org.apache.commons.lang3.StringUtils;
import org.zjvis.datascience.common.constant.IdConstant;
import org.zjvis.datascience.common.dto.TaskDTO;
import org.zjvis.datascience.common.dto.TaskInstanceDTO;

import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static org.zjvis.datascience.common.constant.DataJsonConstant.*;

/**
 * @description TaskInstance相关帮助类，主要便于修改JSON操作
 * @date 2021-12-10
 */
public class TaskInstanceDTOUtil {


    private static final String BUCKET_REGEX = "\\[(.*?)\\]|\\((.*?)\\)";

    /**
     * 每个taskInstance 都有一个默认头  {"inputInfo":{"isSample": .....
     *
     * @param instance
     * @return
     */
    public static JSONObject getInstanceDataJson(TaskInstanceDTO instance) {
        JSONObject taskInsDataJson = JSONObject.parseObject(instance.getDataJson());
        return taskInsDataJson.getJSONObject(INSTANCE_JSON_HEADER);
    }

    public static JSONArray getInputArray(TaskInstanceDTO instance) {
        JSONObject instanceDataJson = getInstanceDataJson(instance);
        if (instanceDataJson.containsKey(INPUT_HEADER)) {
            return instanceDataJson.getJSONArray(INPUT_HEADER);
        } else {
            return new JSONArray();
        }
    }

    public static JSONArray getOutputArray(TaskInstanceDTO instance) {
        JSONObject instanceDataJson = getInstanceDataJson(instance);
        if (instanceDataJson.containsKey(OUTPUT_HEADER)) {
            return instanceDataJson.getJSONArray(OUTPUT_HEADER);
        } else {
            return new JSONArray();
        }
    }

    public static void updateInstanceDataJson(TaskInstanceDTO instance, JSONObject updatedJsonObj) {
        JSONObject result = new JSONObject();
        result.put(INSTANCE_JSON_HEADER, updatedJsonObj);
        instance.setDataJson(result.toJSONString());
    }

    private static Pair<String, Integer> parseBucket(String keyStr) {
        Integer index = null;
        Pattern p = Pattern.compile(BUCKET_REGEX);
        Matcher m = p.matcher(keyStr);
        while (m.find()) {
            index = Integer.parseInt(m.group(1));
            keyStr = m.replaceAll(StringUtils.EMPTY);
        }
        return new Pair<>(keyStr, index);
    }

    private static Pair<String, Object> traverse(String key, Object value) {
        Integer index = null;
        Pattern p = Pattern.compile(BUCKET_REGEX);
        Matcher m = p.matcher(key);
        while (m.find()) {
            index = Integer.parseInt(m.group(1));
            key = m.replaceAll(StringUtils.EMPTY);
        }
        if (value instanceof JSONObject) {
            try {
                if (index == null) {
                    value = ((JSONObject) value).getJSONObject(key);
                } else {
                    value = ((JSONObject) value).getJSONArray(key).get(index);
                }
            } catch (ClassCastException e) {
                if (index == null) {
                    value = ((JSONObject) value).getJSONArray(key);
                } else {
                    value = ((JSONObject) value).getJSONArray(key).get(index);
                }
            } catch (JSONException e) {
                if (index == null) {
                    value = ((JSONObject) value).getOrDefault(key, new JSONObject());
                } else {
                    value = ((JSONObject) value).getOrDefault(key, new JSONArray());
                }
            }
        } else if (value instanceof JSONArray) {
            try {
                if (index == null) {
                    value = ((JSONArray) value).get(0);
                } else {
                    value = ((JSONArray) value).getJSONObject(index);
                }
            } catch (ClassCastException e) {

            }
        }
        return new Pair<>(key, value);
    }

    private static Object getValueByKey(String keyStr, JSONObject dataJson) {
        String[] parts = keyStr.split("\\.");
        Object result = dataJson;
        if (parts.length > 1) {
            for (String key : parts) {
                Pair<String, Object> pair = traverse(key, result);
                result = pair.getValue();
            }
        } else {
            result = dataJson.get(parts[0]);
        }
        return result;
    }

    private static void setValueByKey(Object newValue, String assignedKeyStr, JSONObject dataJson) {
        String[] parts = assignedKeyStr.split("\\.");
        Object result = dataJson;
        if (parts.length > 1) {
            for (int i = 0; i < parts.length; i++) {
                String key = parts[i];
                if (i + 1 == parts.length) {
                    ((JSONObject) result).put(key, newValue);
                } else {
                    Pair<String, Object> pair = traverse(key, result);
                    result = pair.getValue();
                }
            }
        } else if (parts[0].startsWith(PARENT_TIMESTAMP_ARRAY_HEADER)) {
            JSONArray array = null;
            if (((JSONObject) result).containsKey(PARENT_TIMESTAMP_ARRAY_HEADER)) {
                array = ((JSONObject) result).getJSONArray(PARENT_TIMESTAMP_ARRAY_HEADER);
            } else {
                array = new JSONArray();
            }
            Pair<String, Integer> pair = parseBucket(assignedKeyStr);
            array.set(pair.getValue(), newValue);
            ((JSONObject) result).put(PARENT_TIMESTAMP_ARRAY_HEADER, array);
        } else {
            ((JSONObject) result).put(assignedKeyStr, newValue);
        }

    }

    /**
     * 用input信息同步output
     *
     * @param instance
     */
    public static void syncInputAndOutputInfo(TaskInstanceDTO instance) {
        JSONObject jsonObject = JSONObject.parseObject(instance.getDataJson());
    }

    public static void syncInputAndOutputByTaskDTOBySpecificKeys(TaskDTO parentDTO, TaskInstanceDTO childInstance, Map<String, String> map) {

        JSONObject parentDataJson = JSONObject.parseObject(parentDTO.getDataJson());
        JSONObject childDataJson = getInstanceDataJson(childInstance);
        Iterator<Map.Entry<String, String>> entries = map.entrySet().iterator();
        while (entries.hasNext()) {
            Map.Entry<String, String> entry = entries.next();
            Object extractedResult = getValueByKey(entry.getKey(), parentDataJson);
            setValueByKey(extractedResult, entry.getValue(), childDataJson);
        }
        updateInstanceDataJson(childInstance, childDataJson);
    }


    /**
     * 用parent的输出 更新 instance 的输入
     *
     * @param parentDTO
     * @param childInstance
     */
    public static void syncInstanceInputByParentDTO(TaskDTO parentDTO, TaskInstanceDTO childInstance) {
        HashMap<String, String> params = Maps.newHashMap();
        params.put("output[0].tableCols", "input[0].tableCols");
        params.put("output[0].columnTypes", "input[0].columnTypes");
        params.put("lastTimeStamp", "parentTimeStamps[0].");//dot 是为了让它进入getValueByKey
        syncInputAndOutputByTaskDTOBySpecificKeys(parentDTO, childInstance, params);
    }

    public static JSONArray getJsonArrayInJson(TaskInstanceDTO instance, String key) {
        JSONObject instanceDataJson = getInstanceDataJson(instance);
        return (JSONArray) getValueByKey(key, instanceDataJson);
    }

    public static JSONArray getJsonArrayInJson(JSONObject dataJson, String key) {
        return (JSONArray) getValueByKey(key, dataJson);
    }

    public static JSONObject getJsonObjectInJson(TaskInstanceDTO instance, String key) {
        JSONObject instanceDataJson = getInstanceDataJson(instance);
        return (JSONObject) getValueByKey(key, instanceDataJson);
    }


    /**
     * 根据key 去更新datajson
     *
     * @param instance
     * @param key
     * @param newValue
     */
    public static void updateJsonWithSpecificKey(TaskInstanceDTO instance, String key, Object newValue) {
        updateJsonWithSpecificKey(instance, key, newValue, false);
    }


    /**
     * 替换 或者 增量 更新 task datajson 中的值
     *
     * @param instanceDTO
     * @param key
     * @param updateValue
     */
    public static void updateJsonWithSpecificKey(TaskInstanceDTO instanceDTO, String key, Object updateValue, Boolean increment) {
        JSONObject instanceDataJson = TaskInstanceDTOUtil.getInstanceDataJson(instanceDTO);
        if (increment) {

        } else {
            instanceDataJson.put(key, updateValue);
        }
        updateInstanceDataJson(instanceDTO, instanceDataJson);
    }

    public static void setErrorFlagInJson(TaskInstanceDTO instanceDTO) {
        JSONObject instanceDataJson = getInstanceDataJson(instanceDTO);
        setValueByKey("default error msg", IdConstant.INSTANCE_PRECAUTION_ERROR, instanceDataJson);
        updateInstanceDataJson(instanceDTO, instanceDataJson);
    }

    public static void setErrorFlagInJson(TaskInstanceDTO instanceDTO, Object value) {
        JSONObject instanceDataJson = getInstanceDataJson(instanceDTO);
        setValueByKey(value, IdConstant.INSTANCE_PRECAUTION_ERROR, instanceDataJson);
        updateInstanceDataJson(instanceDTO, instanceDataJson);
    }


    public static void main(String[] args) {

        String json = "{\"inputInfo\":{\"isSample\":\"SUCCESS\",\"modelId\":0,\"maxParentNumber\":1,\"autoJoin\":\"yes\",\"forbidden\":false,\"message\":\"以下特征列不含有缺失值： akeed_order_id,\\tpayment_mode,\\tdeliverydistance; 以下特征列不平稳： akeed_order_id\",\"algType\":\"3\",\"parentType\":[2],\"output\":[{\"tableCols\":[\"akeed_order_id\",\"item_count\",\"payment_mode\",\"driver_accepted_time\",\"promo_code\",\"deliverydistance\",\"delivery_time\",\"preparationtime\",\"grand_total\",\"_record_id_\"],\"nodeName\":\"FILTER\",\"totalRow\":135303,\"semantic\":{\"is_favorite\":\"null\",\"created_at\":\"null\",\"promo_code\":\"null\",\"ready_for_pickup_time\":\"null\",\"delivery_time\":\"null\",\"grand_total\":\"null\",\"vendor_rating\":\"null\",\"deliverydistance\":\"null\",\"order_accepted_time\":\"null\",\"item_count\":\"null\",\"payment_mode\":\"null\",\"_record_id_\":\"null\",\"driver_rating\":\"null\",\"cid_x_loc_num_x_vendor\":\"null\",\"location_type\":\"null\",\"picked_up_time\":\"null\",\"vendor_discount_amount\":\"null\",\"delivery_date\":\"null\",\"akeed_order_id\":\"null\",\"promo_code_discount_percentage\":\"null\",\"delivered_time\":\"null\",\"is_rated\":\"null\",\"vendor_id\":\"null\",\"customer_id\":\"null\",\"driver_accepted_time\":\"null\",\"location_number\":\"null\",\"preparationtime\":\"null\"},\"columnTypes\":[\"NUMERIC\",\"NUMERIC\",\"BIGINT\",\"CHARACTER VARYING\",\"CHARACTER VARYING\",\"NUMERIC\",\"DATE\",\"CHARACTER VARYING\",\"NUMERIC\",\"INTEGER\"],\"subType\":0,\"tableName\":\"pipeline.imputation_stat_38818_1637720427591\"}],\"connected\":true,\"input\":[{\"tableCols\":[\"akeed_order_id\",\"item_count\",\"payment_mode\",\"driver_accepted_time\",\"promo_code\",\"deliverydistance\",\"delivery_time\",\"preparationtime\",\"grand_total\",\"_record_id_\"],\"nodeName\":\"FILTER\",\"totalRow\":135303,\"semantic\":{\"is_favorite\":\"null\",\"created_at\":\"null\",\"promo_code\":\"null\",\"ready_for_pickup_time\":\"null\",\"delivery_time\":\"null\",\"grand_total\":\"null\",\"vendor_rating\":\"null\",\"deliverydistance\":\"null\",\"order_accepted_time\":\"null\",\"item_count\":\"null\",\"payment_mode\":\"null\",\"_record_id_\":\"null\",\"driver_rating\":\"null\",\"cid_x_loc_num_x_vendor\":\"null\",\"location_type\":\"null\",\"picked_up_time\":\"null\",\"vendor_discount_amount\":\"null\",\"delivery_date\":\"null\",\"akeed_order_id\":\"null\",\"promo_code_discount_percentage\":\"null\",\"delivered_time\":\"null\",\"is_rated\":\"null\",\"vendor_id\":\"null\",\"customer_id\":\"null\",\"driver_accepted_time\":\"null\",\"location_number\":\"null\",\"preparationtime\":\"null\"},\"columnTypes\":[\"NUMERIC\",\"NUMERIC\",\"BIGINT\",\"CHARACTER VARYING\",\"CHARACTER VARYING\",\"NUMERIC\",\"DATE\",\"CHARACTER VARYING\",\"NUMERIC\",\"INTEGER\"],\"subType\":0,\"tableName\":\"pipeline.view_4269_38514_\"}],\"highlight\":{\"item_count\":[2.506998444790047,2.4773790951638066]},\"lastTimeStamp\":1637545789402,\"parentTimeStamps\":[1637720421461],\"isSimple\":false,\"position\":{\"col\":5,\"row\":2},\"outputCols\":[],\"lastStatus\":\"SUCCESS\",\"algName\":\"缺失值插补-通用\",\"setParams\":[{\"formItem\":[{\"chineseName\":\"特征列\",\"name\":\"cols\",\"type\":\"checkboxGroup\",\"message\":\"特征列必须为数值型时间序列\",\"props\":{\"options\":[{\"label\":\"akeed_order_id\",\"value\":\"akeed_order_id\"},{\"label\":\"item_count\",\"value\":\"item_count\"},{\"label\":\"payment_mode\",\"value\":\"payment_mode\"},{\"label\":\"deliverydistance\",\"value\":\"deliverydistance\"},{\"label\":\"grand_total\",\"value\":\"grand_total\"}]}},{\"chineseName\":\"插补方法\",\"name\":\"method\",\"type\":\"select\",\"props\":{\"options\":[{\"label\":\"同期插补\",\"value\":\"period_fill\"},{\"label\":\"统计插补\",\"value\":\"stat_fill\"},{\"label\":\"线性插补\",\"value\":\"linear_fill\"}]}},{\"chineseName\":\"时间间隔\",\"name\":\"period\",\"rules\":[{\"trigger\":[\"change\",\"blur\"],\"message\":\"输入值范围为1-12(默认以月为单位）\",\"required\":true}],\"type\":\"inputNumber\",\"message\":\"输入值范围为1-12(默认以月为单位）\",\"props\":{\"min\":1,\"max\":12,\"precision\":0}},{\"chineseName\":\"统计方式\",\"name\":\"stat_method\",\"type\":\"select\",\"props\":{\"options\":[{\"label\":\"全局均数\",\"value\":\"mean_fill\"},{\"label\":\"众数\",\"value\":\"mode_fill\"},{\"label\":\"中位数\",\"value\":\"median_fill\"}]}}],\"condition\":[{\"condition\":[[{\"condition\":\"eq\",\"field\":\"method\",\"value\":\"period_fill\"}]],\"actions\":[{\"type\":\"show\",\"target\":\"period\"},{\"type\":\"hidden\",\"target\":\"stat_method\"}]},{\"condition\":[[{\"condition\":\"eq\",\"field\":\"method\",\"value\":\"stat_fill\"}]],\"actions\":[{\"type\":\"hidden\",\"target\":\"period\"},{\"type\":\"show\",\"target\":\"stat_method\"}]},{\"condition\":[[{\"condition\":\"eq\",\"field\":\"method\",\"value\":\"linear_fill\"}]],\"actions\":[{\"type\":\"hidden\",\"target\":\"period\"},{\"type\":\"hidden\",\"target\":\"stat_method\"}]}],\"intro\":\"通用插补：用非缺失数据的均值或者线性特征对缺失值进行插补。\",\"formData\":{\"period\":1,\"stat_method\":\"mean_fill\",\"method\":\"period_fill\",\"cols\":[\"akeed_order_id\",\"item_count\",\"payment_mode\",\"deliverydistance\"]}}]}}";

        JSONObject jsonObject = JSONObject.parseObject(json);
        Object valueByKey = JsonParseUtil.getValueByKey("inputInfo.setParams[0].formItem[0].props.options", jsonObject, List.class);
        System.out.println(valueByKey); //[{"label":"akeed_order_id","value":"akeed_order_id"},{"label":"item_count","value":"item_count"},{"label":"payment_mode","value":"payment_mode"},{"label":"deliverydistance","value":"deliverydistance"},{"label":"grand_total","value":"grand_total"}]

        Object valueByKey2 = JsonParseUtil.getValueByKey("inputInfo.setParams[0].formItem[0].props.options[2]", jsonObject, Map.class);
        System.out.println(valueByKey2); //{"label":"payment_mode","value":"payment_mode"}

    }


}
